#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include "syscall.h"
#include "module.h"
#include "flash.h"
#include "crypto.h"
#include "device.h"
#include "utility.h"
#include "kernel.h"
#include "modmana.h"
#include "clk.h"
#include "prn_drv.h"
#include "config.h"
#include "autoconfig.h"
#include <sys/err.h>
#include "apitrans.h"


#ifdef CONFIG_KMALLOC
#include "kmalloc.h"
#endif

part_t * get_sys_mods_by_name(char * name, uint32_t add,
                uint32_t base, uint32_t size);

static const uint8_t rsa_e[] = {0x01, 0x00, 0x01};

int decode_signature(uint8_t * sig, headinfo_t* app, module_t * pmod)
{
        R_RSA_PUBLIC_KEY publicKey;
        uint8_t mod[MAX_RSA_MODULUS_LEN];
        headinfo_t * phd = (headinfo_t *)mod;
        uint32_t outlen;

        memset(&publicKey,0,sizeof(R_RSA_PUBLIC_KEY));
        publicKey.bits = 2048;

        read_vendor_pk(mod);
        memcpy(publicKey.modulus,mod,MAX_RSA_MODULUS_LEN);
        memcpy(&publicKey.exponent[MAX_RSA_MODULUS_LEN - 3],rsa_e,3);

        RSAPublicDecrypt(mod, &outlen, sig, MAX_RSA_MODULUS_LEN, &publicKey);

        if(phd->head != 0xCC)
                return -1;
        if(phd->hash_flag != 0x2)
                return -1;
        if(phd->public_key != 0x1)
                return -1;
        if(phd->end != 0xCF)
                return -1;

        memcpy(app, phd, sizeof(headinfo_t));

        if (pmod && (phd->firmware_type & (PART_TYPE_MOD >> 8))) {
                memcpy(pmod, mod + sizeof (headinfo_t), sizeof (module_t));
        }
        return 0;
}


int decode_sys_signature(uint8_t * sig, sysheadinfo_t* app)
{
        R_RSA_PUBLIC_KEY publicKey;
        uint32_t outlen;
		uint8_t mod[MAX_RSA_MODULUS_LEN];
        memset(&publicKey,0,sizeof(R_RSA_PUBLIC_KEY));
        publicKey.bits = 2048;

        read_vendor_pk(mod);
        memcpy(publicKey.modulus,mod,MAX_RSA_MODULUS_LEN);
        memcpy(&publicKey.exponent[MAX_RSA_MODULUS_LEN - 3],rsa_e,3);

        rsapublicfunc((uint8_t *)app, &outlen, sig, MAX_RSA_MODULUS_LEN, &publicKey);

	if(memcmp(app->magc, "WITSISOC", 8) != 0)
		return -1;

        return 0;
}


int verify_sysfirmware(part_t * ppart)
{
        uint8_t rsa_header[MAX_RSA_MODULUS_LEN];
        sysheadinfo_t app;
        uint32_t rlen, offset = 0,read_len = 0;

		debug_aux( "Enter into %s()\r\n verify:%s\r\n", __FUNCTION__, ppart->name );
        part_read(ppart, 0, rsa_header, MAX_RSA_MODULUS_LEN);

        if (decode_sys_signature(rsa_header, &app) < 0) {
                return 0;
        }
		debug_aux_hex( "sysheadinfo_t:", &app, sizeof( app ) );
		
        rlen = app.length;
        offset = MAX_RSA_MODULUS_LEN;

        sha2_starts(0);
         while (rlen) {
                read_len = rlen > MAX_RSA_MODULUS_LEN ?
                                MAX_RSA_MODULUS_LEN : rlen;
                if (part_read(ppart, offset, rsa_header, read_len)!= read_len) {
                        return 0;
                }
                sha2_update((uint8_t *)rsa_header, read_len);
                offset += read_len;
                rlen -= read_len;
        }
        sha2_finish(rsa_header);
		debug_aux_hex( "rsa_header:", rsa_header, 32 );
		debug_aux_hex( "app.sha256:", app.sha256, 32 );
		
        if(memcmp(app.sha256, rsa_header, 32)) return 0;
		
        return 1;
}


int verify_firmware_com(int fd, module_t *pmod, headinfo_t *papp)
{
    uint8_t rsa_header[MAX_RSA_MODULUS_LEN];
    headinfo_t app;
    uint32_t rlen, read_len = 0;

    
    if (read(fd, rsa_header, MAX_RSA_MODULUS_LEN) != MAX_RSA_MODULUS_LEN) {
        return -1;
    }
    if (decode_signature(rsa_header, &app, pmod) < 0) {
           return -1;
    }
    sha2_starts(0);
    sha2_update((uint8_t *)&app.hash_flag, MODULE_SHA256_LEN);
    rlen = app.length;
    while (rlen) {
            read_len = rlen > MAX_RSA_MODULUS_LEN ?
                            MAX_RSA_MODULUS_LEN : rlen;
            if (read(fd, rsa_header, read_len) != read_len) {
                    return -1;
            }
            sha2_update((uint8_t *)rsa_header, read_len);
            rlen -= read_len;
    }
    sha2_finish(rsa_header);
    
    if(memcmp(app.sha256, rsa_header, 32)) {
        return -1;
    }
    
    memcpy(papp, &app, sizeof(headinfo_t));
    return (app.length+MAX_RSA_MODULUS_LEN);
}

int verify_firmware3(char * file_name, module_t *pmod, headinfo_t *papp)
{
        int ret;

        int fd = open(file_name, O_RDONLY);
        if (fd < 0) return 0;
        ret = verify_firmware_com(fd, pmod, papp);
       
        close(fd);
        return ret;
}

#ifdef CONFIG_OTA
int verify_firmware3_ota(char * file_name, module_t *pmod, headinfo_t *papp, int lseek_offset)
{
        int ret;

        int fd = open(file_name, O_RDONLY);
        if (fd < 0) return 0;
        if (lseek_offset > 0) {            
            lseek(fd, lseek_offset, SEEK_SET);
        }
        ret = verify_firmware_com(fd, pmod, papp);
       
        close(fd);
        return ret;
}
#endif

int verify_firmware2(part_t * ppart)
{
        uint8_t rsa_header[MAX_RSA_MODULUS_LEN];
        headinfo_t app;
        uint32_t rlen, offset = 0,read_len = 0;

		debug_aux( "Enter into %s()\r\n verify:%s\r\n", __FUNCTION__, ppart->name );
        part_read(ppart, 0, rsa_header, MAX_RSA_MODULUS_LEN);
        if (decode_signature(rsa_header, &app, NULL) < 0) {
                return 0;
        }
        if (app.firmware_type != (ppart->flags >> 8)) return 0;

        rlen = app.length;
        offset = MAX_RSA_MODULUS_LEN;

        sha2_starts(0);
        sha2_update((uint8_t *)&app.hash_flag, MODULE_SHA256_LEN);

        while (rlen) {
                read_len = rlen > MAX_RSA_MODULUS_LEN ?
                                MAX_RSA_MODULUS_LEN : rlen;
                if (part_read(ppart, offset, rsa_header, read_len)!= read_len) {
                        return 0;
                }
                sha2_update((uint8_t *)rsa_header, read_len);
                offset += read_len;
                rlen -= read_len;
        }
        sha2_finish(rsa_header);

        if(memcmp(app.sha256, rsa_header, 32)) {
            return 0;
        }

        return 1;
}

int verify_firmware(char * name)
{
        part_t * ppart;
        if (name == NULL) return 0;
        if ((ppart = get_part_by_name(name, NULL)) == NULL) {
                if ((ppart = get_sys_mods_by_name(name, 0, 0, 0)) == NULL)
                        return 0;
        }
		if(ppart->flags & PART_TYPE_SYS)
		    return verify_sysfirmware(ppart);
		else
        	return verify_firmware2(ppart);
}


extern void *sys_apitrans(int cmd);
module_t * load_module(char *name)
{
	part_t * part;
	module_t * pmod;
    if (name == NULL) return NULL;
	if ((part = get_part_by_name(name, NULL)) == NULL) {
		if ((part = get_sys_mods_by_name(name, 0, 0, 0)) == NULL) {
		    return NULL;

        }
	}

	if (!(part->flags & PART_TYPE_MOD)) {
	        return NULL;
     }
    if (!verify_firmware2(part)) {
        return NULL;
    }

	pmod = (module_t *)(part_base(part) + MAX_RSA_MODULUS_LEN);

	if (pmod->magic_num != MODULE_MAGIC) {
        return NULL;
    }
	uint32_t *psrc, *pdest;
	psrc = (uint32_t *)(pmod->text_end);
	for(pdest = (uint32_t *)(pmod->data_start);
			pdest < (uint32_t *)(pmod->data_end); ) {
		*(pdest ++) = *(psrc ++);
	}

	for(pdest = (uint32_t *)(pmod->bss_start);
		    pdest < (uint32_t *)(pmod->bss_end); ) {
		*(pdest ++) = 0;
	}
	// switch to user mode and startup user application
	user_mode();
    
	pmod->module_init(sys_apitrans);
	// return to KERNEL mode!
	kernel_mode();
	return pmod;
}

void * sys_load_library(char *name)
{
	module_t * pmod = load_module(name);
	if (pmod == NULL) return NULL;

	// switch to user mode and startup user application
	user_mode();
	void *p = pmod->module_export();
	// return to KERNEL mode!
	kernel_mode();
	return p;
}
APITRANS_API_CALL(SYS_LOAD_LIBRARY_API, sys_load_library);

int sys_run_app(char *name)
{
	int ret;
	module_t * pmod = load_module(name);
	if (pmod == NULL) {
        return -1;
    }

	// switch to user mode and startup user application
	user_mode();
	ret = pmod->module_main();
	// return to KERNEL mode!
	kernel_mode();
	return ret;
}
APITRANS_API_CALL(SYS_RUN_APP_API, sys_run_app);




#ifdef RUN_ADRESS_APP
int run_selfapp(char *name)
{
#define Len 256

	int ret;
	part_t * part;
	uint8_t buf[Len];
	uint32_t offset = MAX_RSA_MODULUS_LEN; //filter MAX_RSA_MODULUS_LEN
    volatile uint8_t *p = (volatile uint8_t *)Selftest_Adrress;
	volatile module_t * pdmod = (volatile module_t *)Selftest_Adrress;

    if (name == NULL) return NULL;
	if ((part = get_part_by_name(name, NULL)) == NULL) {
		if ((part = get_sys_mods_by_name(name, 0, 0, 0)) == NULL)
		    return NULL;
	}

	if (!(part->flags & PART_TYPE_MOD))
	        return NULL;

	if (!verify_firmware2(part)) return NULL;
	
    while (part_read(part, offset, buf, Len)) {
		memcpy(p, buf, Len);
		p+= Len;
		offset += Len;
		if (offset > part->size)break;
    }
	
	if (pdmod->magic_num != MODULE_MAGIC) return NULL;

    pdmod->module_init(sys_apitrans);

	user_mode();
	ret = pdmod->module_main();	
	kernel_mode();
	
	return ret;
}
#endif
